/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package piplayer.impl.pi;

import java.io.IOException;
import piplayer.intrface.TrackPlayerEventHandler;
import piplayer.intrface.TrackPlayer;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Queue;
import java.util.ArrayDeque;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author richb
 */
public class Mpg123TrackPlayer implements TrackPlayer
{
    private final Object _mainMethodsLock = new Object();
    private final String _mpgProcName;
    
    private volatile TrackPlayerEventHandler _handler = null;
    private volatile boolean _mainThreadStayAlive = false;
    private volatile Thread _playerProcMonitor = null;
    private volatile int _nextCommand = 0;
    /*
        0 => none
        1 => play
        2 => pause
    */
    private volatile String _playCmdFile;
    private volatile int _playCmdSeekStart;
    
    public Mpg123TrackPlayer(String procName)
    {
        this._mpgProcName = procName;
    }

    @Override
    public void begin()
    {
        synchronized(this._mainMethodsLock)
        {
            this._mainThreadStayAlive = true;
            
            this._playerProcMonitor = new Thread(
                new Runnable()
                {
                    public void run()
                    {
                        Mpg123TrackPlayer.this.procMonitor();
                        System.out.println("Thread in Mpg123TrackPlayer returning from run().");
                    }
                });
            
            this._playerProcMonitor.start();
        }
    }

    @Override
    public void end()
    {
        this._mainThreadStayAlive = false;
    }

    @Override
    public boolean playTrack(String filename, int beginAt)
    {
        synchronized(this._mainMethodsLock)
        {
            if (this._playerProcMonitor != null)
            {
                this._nextCommand = 1;
                this._playCmdFile = filename;
                this._playCmdSeekStart = beginAt;
                return true;
            }
            else
            {
                return false;
            }   
        }
    }
    
    @Override
    public boolean pauseTrack()
    {
        synchronized(this._mainMethodsLock)
        {
            if (this._playerProcMonitor != null)
            {
                this._nextCommand = 2;
                this._playCmdFile = null;
                this._playCmdSeekStart = 0;
                return true;
            }
            else
            {
                return false;
            }   
        }
    }

    @Override
    public boolean setEventHandler(TrackPlayerEventHandler e)
    {
        synchronized(this._mainMethodsLock)
        {
            this._handler = e;
        }
        
        return true;
    }
    
    private void procMonitor()
    {
        
        /* Start mpgNNN player process */
        
        final ProcessBuilder pb = new ProcessBuilder();
        final StringBuilder sb = new StringBuilder(1024);
        final byte[] byteBuffer = new byte[1024];
        final char[] charBuffer = new char[1024];
        final Pattern fPattern = Pattern.compile("@F (\\d+) (\\d+) (\\d+\\.\\d+) (\\d+\\.\\d+)\\n");
        
        pb.command().add(this._mpgProcName);
        pb.command().add("-R");
        pb.command().add("plopadop"); /* Needed for -R mode; a random word after the -R */
        
        pb.redirectErrorStream(true); /* stderr goes out of stdout */
        
        final Process proc;
        
        try
        {
            proc = pb.start();
        }
        catch (IOException e)
        {
            throw new Error(e);
        }
        
        final OutputStream os = proc.getOutputStream();
        final InputStream is = proc.getInputStream();
        
        final PrintWriter pw = new PrintWriter(os, true);
        
        while (this._mainThreadStayAlive)
        {
            
            System.out.println("Thread in Mpg123TrackPlayer tick.");
            
            /* See if it's still alive */
            if (proc.isAlive())
            {
                
            }
            else
            {
                System.err.println("Proc has died.");
            }
            
            if (pw.checkError())
            {
                System.err.println("pw has error");
            }
            
            final int cmd;
            final String filename;
            final int offset;
            
            /* Process any commands */
            synchronized(this._mainMethodsLock)
            {
                cmd = this._nextCommand;
                filename = this._playCmdFile;
                offset = this._playCmdSeekStart;
            }
            
            switch (cmd)
            {
                case 0:
                    break;
                case 1:
                    pw.println("LOAD " + filename);
                    pw.flush();
                    break;
                case 2:
                    pw.println("PAUSE");
                    pw.flush();
                    break;
                default:
                    throw new Error("Unhandled command code");
            }
            
            synchronized(this._mainMethodsLock)
            {
                this._nextCommand = 0;
                this._playCmdFile = null;
                this._playCmdSeekStart = 0;
            }
            
            /* Read all chars from stream and do some regexing */
            final int bytesRead;
            
            try
            {
                bytesRead = is.read(byteBuffer); /* Max is array size */
            }
            catch (IOException e)
            {
                throw new Error(e);
            }
            
            for (int i = 0; i < bytesRead; i++)
            {
                charBuffer[i] = (char) byteBuffer[i];
            }
            
            sb.append(charBuffer, 0, bytesRead);
            
            /* Tease out patterns */
            if (sb.length() > 0)
            {
                boolean found = false;
                
                /* If string buffer not at the beginning of a @x then delete up to first @x */
                
                final int firstAtIndex = sb.indexOf("@");
                
                if (firstAtIndex > 0)
                {
                    /* Delete chars up to @ */
                    sb.delete(0, firstAtIndex);
                    
                    /* Stream synced to next @ */
                }
            
                do
                {
                    final Matcher fMatch = fPattern.matcher(sb);
                    
                    if (fMatch.find())
                    {
                        
                        
                    }
                }
                while (found == true);
                
            }
            
            /* Clear down, temporary measure */
            sb.delete(0, sb.length());
            
            /* Sleep for a while ... */
            try
            {
                Thread.sleep(100); /* 0.1s */
            }
            catch (InterruptedException e)
            {
                
            }
            
        }
        
        /* Exit */
        synchronized(this._mainMethodsLock)
        {
            int quitCounter = 0;
            final int quitLimit = 5;
            
            System.out.println("Thread in Mpg123TrackPlayer quitting.");
            
            while (proc.isAlive() && quitCounter < quitLimit)
            {
                pw.println("QUIT");
                pw.flush();
                quitCounter++;
                
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException e)
                {
                    /* Meh */
                }
            }
            
            /* Kill it if necessary */
            if (quitCounter >= quitLimit)
            {
                proc.destroy();
            }
            
            this._playerProcMonitor = null;
            this._nextCommand = 0;
            this._playCmdFile = null;
            this._playCmdSeekStart = 0;
        }        
        
    }
    
}
